import type { AxiosInstance, AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig, Method } from "axios";
import axios from "axios";
import { notification } from "@/hooks/modules/useToast";
import { blobValidate, checkStatus } from "@/utils";
import { useUserStore } from "@/store/user";
import { useMenuStore } from "@/store/menus";
import { useNavigateCacheRouter } from "@/hooks/modules/useRouter";
import { getCookieToken, removeCookieToken } from "@/utils/cookies";
import { antdSpin } from "antd-spin";
import { saveAs } from "file-saver";

const _config: AxiosInstance = axios.create({
  baseURL: import.meta.env.VITE_APP_BASE_URL,
  timeout: import.meta.env.VITE_APP_AXIOS_TIMEOUT,
  withCredentials: false
});

class RequestHttp {
  #requestInstance: AxiosInstance;
  constructor(_config: AxiosInstance) {
    this.#requestInstance = _config;

    this.#requestInstance.interceptors.request.use(
      (config: InternalAxiosRequestConfig) => {
        config.headers.Authorization = `Bearer ${getCookieToken()}`;
        return config;
      },
      (error) => Promise.reject(error)
    );

    this.#requestInstance.interceptors.response.use(
      (response: AxiosResponse) => {
        const code = Number.parseInt(response.data.code || response.data.status) || 200;
        const msg = response.data.msg || response.data.message;
        if (code === 401 || code === 403) {
          notification.warning({ message: "当前登录身份已过期，请重新登录！" });
          const { clearUserInfo } = useUserStore.getState();
          const { clearAuthMenu } = useMenuStore.getState();
          const { getNavigateCache } = useNavigateCacheRouter();
          clearUserInfo();
          clearAuthMenu();
          removeCookieToken();
          getNavigateCache("/login", { replace: true });
          return Promise.reject("当前登录身份已过期，请重新登录！");
        }
        if (code > 200 || code < 200) {
          notification.error({ message: msg });
          return Promise.reject(msg);
        }
        return response.data;
      },
      (error) => {
        const { response } = error;
        // 请求超时 && 网络错误单独判断，没有 response
        if (error.message.includes("Network Error")) notification.error({ message: "网络错误！请您稍后重试" });
        if (error.message.includes("timeout")) notification.error({ message: "请求超时！请您稍后重试" });
        // 根据服务器响应的错误状态码，做不同的处理
        if (response) checkStatus(response.status);
        return Promise.reject(error);
      }
    );
  }

  get<T = any>(url: string, params: Record<string, any> = {}, config: AxiosRequestConfig = {}): Promise<T> {
    return this.#requestInstance.get(url, { params, ...config });
  }

  delete<T = any>(url: string, params: Record<string, any> = {}, config: AxiosRequestConfig = {}): Promise<T> {
    return this.#requestInstance.delete(url, { params, ...config });
  }

  post<T = any>(url: string, data: Record<string, any> = {}, config: AxiosRequestConfig = {}): Promise<T> {
    return this.#requestInstance.post(url, data, config);
  }

  put<T = any>(url: string, data: Record<string, any> = {}, config: AxiosRequestConfig = {}): Promise<T> {
    return this.#requestInstance.put(url, data, config);
  }

  async exportFile(option: { method?: Method; url: string; params?: Record<string, any>; filename: string; _config?: AxiosRequestConfig }) {
    const { method = "get", url, params = {}, filename, _config } = option;
    const loadingInstance = antdSpin.service({
      fullscreen: true,
      background: "rgba(0, 0, 0, 0.2)",
      spinProps: { size: "large" }
    });
    try {
      const resp = (await this.#requestInstance({
        method,
        url,
        ...(method === "get" ? { params } : { data: params }),
        ...{
          responseType: "blob",
          ..._config
        }
      })) as unknown as Blob;

      const isBlob: boolean = blobValidate(resp);
      if (isBlob) {
        const blob: Blob = new Blob([resp]);
        saveAs(blob, filename);
      } else {
        const resText = await resp.text();
        const rspObj = JSON.parse(resText);
        // @ts-expect-error rspObj
        const errMsg = checkStatus(rspObj.code) || rspObj.msg;
        notification.error({ message: errMsg });
      }
      loadingInstance.close();
    } catch {
      notification.error({ message: "导出文件失败！" });
      loadingInstance.close();
    }
  }
}

export const Request = new RequestHttp(_config);
